﻿using System;
using System.Text;

namespace Miao.ML
{
    internal class Parser
    {
        private ref struct DataStructureContext
        {
            internal int index;

            internal int start;

            internal int length;

            internal int caretCount;
            //左标志计数
            internal int leftSignCount;
            //右标志计数
            internal int rightSignCount;

            internal int doubleQuotesCount;

            internal ReadOnlySpan<char> sentenceSpan;

            internal readonly int sentenceLength;

            internal readonly bool canAnalyze => index < sentenceLength;

            internal readonly bool signSymmetry
            {
                get
                {
                    if (leftSignCount == rightSignCount)
                    {
                        return (caretCount & 1) == 0;
                    }
                    return false;
                }
            }

            internal DataStructureContext(ReadOnlySpan<char> sentenceSpan)
            {
                index = 0;
                start = 0;
                length = 0;
                leftSignCount = 0;
                rightSignCount = 0;
                caretCount = 0;
                doubleQuotesCount = 0;
                this.sentenceSpan = sentenceSpan;
                sentenceLength = sentenceSpan.Length;
            }
        }

        private readonly StringBuilder stringBuilder = new StringBuilder(128);
        /// <summary>
        /// 按逗号分析数据结构
        /// </summary>
        /// <param name="dataStructureContext"></param>
        /// <returns></returns>
        private static ReadOnlySpan<char> AnalyzeDataStructureByComma(ref DataStructureContext dataStructureContext)
        {
            int index = dataStructureContext.index;
            while (dataStructureContext.index < dataStructureContext.sentenceLength)
            {
                char c = dataStructureContext.sentenceSpan[dataStructureContext.index];
                if (c == Keyword.doubleQuotes && (dataStructureContext.index == index || dataStructureContext.sentenceSpan[dataStructureContext.index - 1] != '\\'))
                {
                    dataStructureContext.doubleQuotesCount++;
                }
                else if ((dataStructureContext.doubleQuotesCount & 1) == 0)
                {
                    switch (c)
                    {
                        case Keyword.caret:
                            dataStructureContext.caretCount++;
                            break;
                        case '(':
                        case '<':
                        case '[':
                        case '{':
                        case '【':
                            dataStructureContext.leftSignCount++;//这个看起来是记住层数让解析逗号时忽略x段落。
                            break;
                        case ')':
                        case '>':
                        case ']':
                        case '}':
                        case '】':
                            dataStructureContext.rightSignCount++;
                            break;
                        case ',':
                            if (dataStructureContext.signSymmetry)
                            {
                                ReadOnlySpan<char> result = dataStructureContext.sentenceSpan.Slice(dataStructureContext.start, dataStructureContext.length);
                                dataStructureContext.index++;
                                dataStructureContext.length = 0;
                                dataStructureContext.start = dataStructureContext.index;
                                return result;
                            }
                            break;
                    }
                }
                dataStructureContext.length++;
                dataStructureContext.index++;
            }
            return dataStructureContext.sentenceSpan.Slice(dataStructureContext.start, dataStructureContext.length);
        }

        private static void SplitKeyValuePair(ReadOnlySpan<char> str, out ReadOnlySpan<char> key, out ReadOnlySpan<char> value)
        {
            int num = 0;
            for (int i = 0; i < str.Length; i++)
            {
                switch (str[i])
                {
                    case Keyword.doubleQuotes:
                        if (i == 0 || str[i - 1] != '\\')
                        {
                            num++;
                        }
                        break;
                    case ':':
                        if ((num & 1) == 0)
                        {
                            key = str.Slice(0, i);
                            int num2 = i + 1;
                            value = str.Slice(num2, str.Length - num2);
                            return;
                        }
                        break;
                }
            }
            throw new Exception(string.Format("{0}.{1}: 无法在键值对语句 {2} 中找到分隔符 {3}, 解析失败", "Parser", "SplitKeyValuePair", str.ToString(), ':'));
        }
        /// <summary>
        /// 依照{separator}分割
        /// </summary>
        /// <param name="str"></param>
        /// <param name="separator"></param>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <exception cref="Exception"></exception>
        private static void SplitOnce(ReadOnlySpan<char> str, char separator, out ReadOnlySpan<char> one, out ReadOnlySpan<char> two)
        {
            for (int i = 0; i < str.Length; i++)
            {
                if (str[i] == separator)
                {
                    one = str.Slice(0, i);
                    int num = i + 1;
                    two = str.Slice(num, str.Length - num);
                    return;
                }
            }
            throw new Exception(string.Format("{0}.{1}: 无法在语句 {2} 中找到分隔符 {3}, 解析失败", "Parser", "SplitOnce", str.ToString(), separator));
        }
        private static bool AnalyzeCustomData(CustomData customData, int index, ref ReadOnlySpan<char> sentenceSpan)
        {
            SplitOnce(sentenceSpan.Slice(0, index), '&', out var one, out var two);
            customData.qualifiedName = one.ToString();
            if (two.SequenceEqual("null"))
            {
                customData.isNull = true;
                return false;
            }
            customData.referenceID = int.Parse(two);
            int num = index + 1;
            sentenceSpan = sentenceSpan.Slice(num, sentenceSpan.Length - 1 - num);
            return sentenceSpan.Length != 0;
        }
        public WinMLData ParseData(string winMLString)
        {
            return RecursiveAnalyze(winMLString);
        }

        private WinMLData RecursiveAnalyze(ReadOnlySpan<char> sentenceSpan)
        {
            if (sentenceSpan.IsEmpty)
            {
                throw new Exception("Parser.RecursiveAnalyze: 出现了一个空文本, 请检查winML的格式是否正确!");
            }
            if (sentenceSpan.SequenceEqual("null"))
            {
                return new StringData(null);
            }
            if (sentenceSpan[0] == Keyword.doubleQuotes)
            {
                if (sentenceSpan[sentenceSpan.Length - 1] == Keyword.doubleQuotes)
                {
                    ref ReadOnlySpan<char> reference = ref sentenceSpan;
                    sentenceSpan = reference.Slice(1, reference.Length - 1 - 1);
                    return new StringData((sentenceSpan.Length == 0) ? string.Empty : Escape(sentenceSpan));
                }
            }
            if (sentenceSpan.SequenceEqual("true") || sentenceSpan.SequenceEqual("false"))
            {
                return new BoolData(bool.Parse(sentenceSpan));
            }
            int keywordNum = -1;
            for (int i = 0; i < sentenceSpan.Length; i++)
            {
                switch (sentenceSpan[i])//先提取到符号表关键字
                {
                    case '&':
                        keywordNum = i;
                        break;
                    case '*':
                        return new ReferenceData(int.Parse(sentenceSpan.Slice(0, i)));
                    case '{':
                        {
                            ObjectData objectData = new ObjectData();
                            if (AnalyzeReferenceObject(objectData, i, ref sentenceSpan))
                            {
                                DataStructureContext dataStructureContext = new DataStructureContext(sentenceSpan);
                                while (dataStructureContext.canAnalyze)
                                {
                                    ReadOnlySpan<char> comma = AnalyzeDataStructureByComma(ref dataStructureContext);
                                    SplitOnce(comma, '=', out var one, out var two);//这里结构体数据也是一个名称=特定类型，直到最基本数据。
                                    if (two[0] == '?')
                                    {
                                        ref ReadOnlySpan<char> reference = ref two;
                                        two = reference.Slice(1, reference.Length - 1);
                                        objectData.AddProperty(one.ToString(), two.SequenceEqual("null") ? new NullableData() : new NullableData(RecursiveAnalyze(two)));
                                    }
                                    else//给对象添加子类的winML数据。
                                    {
                                        WinMLData winMLData = RecursiveAnalyze(two);//递归解析咯
                                        objectData.AddProperty(one.ToString(), winMLData);
                                    }
                                }
                            }
                            return objectData;
                        }
                    case '['://list有专门的包裹机制
                        {
                            ListData listData = new ListData();
                            if (AnalyzeReferenceObject(listData, i, ref sentenceSpan))//这一步sentenceSpan是完整的<System.Int32>&0[1230,321,444]
                            {
                                DataStructureContext dataStructureContext = new DataStructureContext(sentenceSpan);
                                while (dataStructureContext.canAnalyze)
                                {
                                    listData.AddElement(RecursiveAnalyze(AnalyzeDataStructureByComma(ref dataStructureContext)));
                                }
                            }
                            return listData;
                        }
                    case '('://Dict也有专门的包裹机制
                        {
                            DictionaryData dictionaryData = new DictionaryData();
                            if (AnalyzeReferenceObject(dictionaryData, i, ref sentenceSpan))
                            {
                                DataStructureContext dataStructureContext3 = new DataStructureContext(sentenceSpan);
                                while (dataStructureContext3.canAnalyze)
                                {
                                    SplitKeyValuePair(AnalyzeDataStructureByComma(ref dataStructureContext3), out var key, out var value);
                                    dictionaryData.Add(RecursiveAnalyze(key), RecursiveAnalyze(value));
                                }
                            }
                            return dictionaryData;
                        }
                    case '【':
                        {
                            GenericMulticastMethodData multicastMethodData = new GenericMulticastMethodData();
                            //AnalyzeGenericName(0, one, genericReferenceObjectData);//解析泛型
                            ReadOnlySpan<char> actionTypeName = sentenceSpan.Slice(0, i);
                            multicastMethodData.qualifiedName = actionTypeName.ToString();//先赋非泛型，再由下方泛型的覆盖
                            AnalyzeGenericName(0, actionTypeName, multicastMethodData);
                            //multicastMethodData.delegateType = Type.GetType(actionTypeName.ToString());
                            //剥掉【】暴露出数据
                            ReadOnlySpan<char> list = sentenceSpan.Slice(actionTypeName.Length+1, sentenceSpan.Length - actionTypeName.Length - 2);


                            DataStructureContext dataStructureContext = new DataStructureContext(list);
                            while (dataStructureContext.canAnalyze)
                            {
                                ReadOnlySpan<char> action = AnalyzeDataStructureByComma(ref dataStructureContext);
                                SplitOnce(action, '|', out var typeName, out var methodName);
                                MethodData methodData = new MethodData(methodName.ToString());
                                WinMLData winMLData = RecursiveAnalyze(typeName);
                                methodData.AddProperty(winMLData);
                                multicastMethodData.AddMethod(methodData);
                            }
                                //这一段分出System.Action和&-1，但实际有问题。
                                //SplitOnce(,'【', out var one, out var two);


                                //int num = i + 1;
                                //sentenceSpan = sentenceSpan.Slice(num, sentenceSpan.Length - 1 - num);
                                //AnalyzeDataStructureByComma(two);不能用它

                                //SplitOnce(sentenceSpan, '|', out var typeName, out var methodName);

                            //MethodData methodData = new MethodData(methodName.ToString());
                            //MethodData methodData = new MethodData(typeName.ToString(), methodName.ToString());
                            //WinMLData winMLData = RecursiveAnalyze(typeName);
                            //methodData.AddProperty(winMLData);

                            return multicastMethodData;
                        }
                    case Keyword.caret:
                        {
                            CustomData customData = new CustomData();
                            customData.dataText = (AnalyzeCustomData(customData, i, ref sentenceSpan) ? sentenceSpan.ToString() : string.Empty);
                            return customData;
                        }
                }
            }
            if (keywordNum > 0)
            {
                string qualifiedName = sentenceSpan.Slice(0, keywordNum).ToString();
                ref ReadOnlySpan<char> reference = ref sentenceSpan;
                int num2 = keywordNum + 1;
                return new EnumData(qualifiedName, reference.Slice(num2, reference.Length - num2).ToString());
            }
            switch (sentenceSpan[sentenceSpan.Length - 1])
            {
                case 'd':
                    {
                        ref ReadOnlySpan<char> reference = ref sentenceSpan;
                        return new DoubleData(double.Parse(reference.Slice(0, reference.Length - 1)));
                    }
                case 'f':
                    {
                        ref ReadOnlySpan<char> reference = ref sentenceSpan;
                        return new FloatData(float.Parse(reference.Slice(0, reference.Length - 1)));
                    }
                case 'L':
                    {
                        ref ReadOnlySpan<char> reference = ref sentenceSpan;
                        return new LongData(long.Parse(reference.Slice(0, reference.Length - 1)));
                    }
                default:
                    return new IntData(int.Parse(sentenceSpan));
            }
        }
        /// <summary>
        /// 解析是否是引用对象
        /// </summary>
        /// <param name="genericReferenceObjectData"></param>
        /// <param name="index"></param>
        /// <param name="sentenceSpan"></param>
        /// <returns></returns>
        private bool AnalyzeReferenceObject(GenericReferenceObjectData genericReferenceObjectData, int index, ref ReadOnlySpan<char> sentenceSpan)
        {
            //分割名称和索引,one是类型,two是当前类型序号
            SplitOnce(sentenceSpan.Slice(0, index), '&', out var one, out var two);
            stringBuilder.Length = 0;
            AnalyzeGenericName(0, one, genericReferenceObjectData);//只是解析泛型
            genericReferenceObjectData.qualifiedName = stringBuilder.ToString();
            if (two.SequenceEqual("null"))
            {
                genericReferenceObjectData.isNull = true;
                return false;
            }
            genericReferenceObjectData.referenceID = int.Parse(two);
            int num = index + 1;
            sentenceSpan = sentenceSpan.Slice(num, sentenceSpan.Length - 1 - num);//拆离并暴露数据
            return sentenceSpan.Length != 0;
        }
        /// <summary>
        /// 解析泛型参数
        /// </summary>
        /// <param name="startIndex"></param>
        /// <param name="typeInfoSpan"></param>
        /// <param name="generic"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private int AnalyzeGenericName(int startIndex, ReadOnlySpan<char> typeInfoSpan, IGeneric generic)
        {
            for (int i = startIndex; i < typeInfoSpan.Length; i++)
            {
                switch (typeInfoSpan[i])//查找有无需解析泛型
                {
                    case '<':
                        {
                            int num = startIndex;
                            generic.openTypeName = typeInfoSpan.Slice(num, i - num).ToString();
                            StringBuilder obj2 = stringBuilder;
                            num = startIndex;
                            obj2.Append(typeInfoSpan.Slice(num, i + 1 - num));
                            while (true)
                            {
                                GenericArgumentData generic2 = new GenericArgumentData();
                                i = AnalyzeGenericName(i + 1, typeInfoSpan, generic2);
                                generic.AddGenericArgument(generic2);
                                char c;
                                stringBuilder.Append(c = typeInfoSpan[i]);
                                switch (c)
                                {
                                    case ',':
                                        break;
                                    case '>':
                                        return i + 1;
                                    default:
                                        throw new Exception("Parser.AnalyzeGenericName: 泛型格式错误!");
                                }
                            }
                        }
                    case ',':
                    case '>':
                        {
                            int num = startIndex;
                            generic.qualifiedName = typeInfoSpan.Slice(num, i - num).ToString();
                            StringBuilder obj = stringBuilder;
                            num = startIndex;
                            obj.Append(typeInfoSpan.Slice(num, i - num));
                            return i;
                        }
                }
            }
            stringBuilder.Append(typeInfoSpan);
            return typeInfoSpan.Length - 1;
        }

        private string Escape(ReadOnlySpan<char> textSpan)
        {
            stringBuilder.Length = 0;
            for (int i = 0; i < textSpan.Length; i++)
            {
                char c = textSpan[i];
                if (c != '\\')
                {
                    stringBuilder.Append(c);
                }
                else if (i <= textSpan.Length - 2)
                {
                    switch (textSpan[i + 1])
                    {
                        case 'r':
                            stringBuilder.Append('\r');
                            i++;
                            break;
                        case 'n':
                            stringBuilder.Append('\n');
                            i++;
                            break;
                        case '\\':
                            stringBuilder.Append('\\');
                            i++;
                            break;
                        case Keyword.doubleQuotes:
                            stringBuilder.Append(Keyword.doubleQuotes);
                            i++;
                            break;
                    }
                }
            }
            return stringBuilder.ToString();
        }
    }
}
